# -*- coding: utf-8 -*-

from hlm.node import *
from hlm.channel import ChannelProperty as cp

from ip.opdef import AluOpCode


class ChannelAlu(Channel):
    """ The ALU Channel provided the communication ports for the ALU node.

    """
    lanes_map = {
        'Src': {
            cp.PORTS: [('addr', -1, int),
                       ('data0', -1, complex), ('data1', -1, complex),
                       ('op', -1, int)],
            cp.GET: ['addr', 'data0', 'data1', 'op']
        },
        'Dst': {
            cp.PORTS: [('addr', -1, int),
                       ('data', -1, complex)],
            cp.GET: ['addr', 'data'],
            cp.REVERSE: True
        }
    }


class NodeAlu(Node):
    """ The ALU node provide the ability to calculate the value based on the
        provided instructions.
    """
    def initialize(self, params: ObjectDict) -> None:
        # Create the channel ports.
        self.port.receiver = self.port_channel_receiver(ChannelAlu)

        self.reg.dst_addr = 0
        self.reg.src_0 = 0j
        self.reg.src_1 = 0j
        self.reg.dst_data = 0j
        self.reg.op_code = AluOpCode.NOP

    tasks = ['task_update']

    def task_update(self):
        # The update task needs to extract the operation, compute the result and
        # set the destination value.  Check new operation.
        if self.port.receiver.Src.is_any_empty():
            # No operation detected.
            return
        # Extract the operation.
        self.reg.dst_addr, self.reg.src_0, self.reg.src_1, self.reg.op_code = \
            self.port.receiver.Src.get()
        # Based on the op code, get the result.
        if self.reg.op_code == AluOpCode.ADD:
            self.reg.dst_data = self.reg.src_0 + self.reg.src_1
        elif self.reg.op_code == AluOpCode.SUB:
            self.reg.dst_data = self.reg.src_0 - self.reg.src_1
        elif self.reg.op_code == AluOpCode.MUL:
            self.reg.dst_data = self.reg.src_0 * self.reg.src_1
        elif self.reg.op_code == AluOpCode.DIV:
            self.reg.dst_data = self.reg.src_0 / self.reg.src_1
        else:
            self.reg.dst_data = 0
        # Write to the target address.
        self.port.receiver.Dst.put(addr=self.reg.dst_addr, data=self.reg.dst_data)
